package com.wtzz.police.base.service.impl;

import com.wtzz.police.base.bean.dto.UserDto;
import com.wtzz.police.base.bean.req.UserFilterDto;
import com.wtzz.police.base.bean.dto.UserInfoDto;
import com.wtzz.police.base.entity.Department;
import com.wtzz.police.base.entity.User;
import com.wtzz.police.base.model.enums.Role;
import com.wtzz.police.base.model.enums.WtzzResponse;
import com.wtzz.police.base.model.exception.WtzzRuntimeException;
import com.wtzz.police.base.repository.DepartmentRepository;
import com.wtzz.police.base.repository.base.BaseRepository;
import com.wtzz.police.base.util.RepoHelper;
import com.wtzz.police.base.repository.UserRepository;
import com.wtzz.police.base.service.base.BaseCurdServiceImpl;
import com.wtzz.police.base.service.UserService;
import com.wtzz.police.base.util.WebTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.KeyValue;
import org.apache.commons.collections4.keyvalue.DefaultKeyValue;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@Slf4j
@Transactional
public class UserServiceImpl extends BaseCurdServiceImpl<User, String> implements UserService, RepoHelper {

    @Autowired
    private UserRepository repo;
    @Autowired
    private DepartmentRepository deptRepo;

    private PasswordEncoder encoder = NoOpPasswordEncoder.getInstance();

    @Override
    protected BaseRepository<User, String> getRepository() {
        return repo;
    }

    @Override
    public void add(Object data) {
        User user = mapper.map(data, User.class);
        Department department = null;
        if (data instanceof UserDto) {
            if (((UserDto) data).getDepartmentId() != null) {
                department = deptRepo.findByIdOrNull(((UserDto) data).getDepartmentId());
            }
        }
        user.setDepartment(department);
        getRepository().save(user);
        log.info("新增{}", getRepository().getEntityDesc());
    }

    @Override
    public void update(String id, Object data) {
        User user = repo.findByIdOrThrow(id);
        mapper.map(user, data);
        Department department = null;
        if (data instanceof UserDto) {
            if (((UserDto) data).getDepartmentId() != null) {
                department = deptRepo.findByIdOrNull(((UserDto) data).getDepartmentId());
            }
        }
        user.setDepartment(department);
        getRepository().save(user);
        log.info("更新{}, 其ID为:{}", getRepository().getEntityDesc(), id);
    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {
        User u = WebTool.getCurrentUser();
        if (StringUtils.isNotEmpty(oldPassword) && StringUtils.isNotEmpty(newPassword)) {
            if (encoder.matches(oldPassword, u.getPassword())) {
                findNotDeleted(u.getId()).setPassword(encoder.encode(newPassword));
            } else {
                throw new WtzzRuntimeException(WtzzResponse.INVALID_PASSWORD);
            }
        } else {
            throw new WtzzRuntimeException(WtzzResponse.PASSWORD_IS_NULL);
        }
        log.info("用户{}更改了密码", u.getUsername());
    }

    @Override
    public void resetPassword(String id) {
        User user = findNotDeleted(id);
        user.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        log.info("用户{}重置密码", user.getUsername());
    }

    @Override
    public Optional<User> findByUsername(String username) {
        return repo.findByUsername(username);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return findByUsername(username).orElseThrow(() -> new WtzzRuntimeException(WtzzResponse.NO_USER_EXIST));
    }

    @Override
    public void setEnabled(String id, Boolean enable) {
        findNotDeleted(id).setEnabled(enable);
    }

    @Override
    public Page<UserInfoDto> listNotDeleted(Pageable pageable, UserFilterDto userFilterDto) {
        String innerSql = "select u.id,u.username,u.fullname,u.phone,u.role," +//
                "u.enabled,u.department_name departmentName  " +//
                "from gs_user u  where u.deleted=0";
        List<String> conditions = new ArrayList<>();
        List<KeyValue<String, Object>> params = new ArrayList<>();
        if (userFilterDto.getRole() != null) {
            conditions.add("u.role=:role");
            params.add(new DefaultKeyValue<>("role", userFilterDto.getRole().name()));
        }
        if (StringUtils.isNotBlank(userFilterDto.getUsername())) {
            conditions.add("u.username=:username");
            params.add(new DefaultKeyValue<>("username", userFilterDto.getUsername()));
        }
        if (StringUtils.isNotBlank(userFilterDto.getFullname())) {
            conditions.add("u.fullname like :fullname");
            params.add(new DefaultKeyValue<>("fullname", wrapLike(userFilterDto.getFullname())));
        }
        innerSql += generateAndConditions(conditions, "and");
        String countSql = wrapSelectCount(innerSql, "data");
        long total = ((Number) bindParams(em.createNativeQuery(countSql), params).getSingleResult()).longValue();
        NativeQuery query = bindParams(em.createNativeQuery(innerSql), params).setMaxResults(pageable.getPageSize())
                                                                              .setFirstResult(pageable.getPageNumber() * pageable.getPageSize())
                                                                              .unwrap(NativeQuery.class);
        List list = addScalarToQuery(query, UserInfoDto.class).addScalar("role", createEnumTypeScale(em, Role.class, true))
                                                              .setResultTransformer(Transformers.aliasToBean(UserInfoDto.class))
                                                              .list();
        return new PageImpl<UserInfoDto>(list, pageable, total);
    }

}
